home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48_2 / reversi.sou < prev    next >
Text File  |  1995-03-23  |  35KB  |  945 lines

  1. Article 3005 of comp.sys.handhelds:
  2. Path: en.ecn.purdue.edu!noose.ecn.purdue.edu!samsung!munnari.oz.au!brolga!uqcspe!batserver.cs.uq.oz.au!grue
  3. From: grue@batserver.cs.uq.oz.au (Frobozz)
  4. Newsgroups: comp.sys.handhelds
  5. Subject: Reversi assembly sources
  6. Keywords: reversi
  7. Message-ID: <6487@uqcspe.cs.uq.oz.au>
  8. Date: 20 Dec 90 23:50:12 GMT
  9. Sender: news@uqcspe.cs.uq.oz.au
  10. Reply-To: grue@batserver.cs.uq.oz.au
  11. Organization: Computer Science Department, The University of Queensland, Brisbane, Australia
  12. Lines: 929
  13.  
  14. hiya,
  15.     I've decided to post my current version of the assembly code portions
  16. of my just released reversi program.  I will note that the comments are
  17. slightly out of date and are in some places less than meaningfull (and I
  18. haven't got round to re-doing them).  This will be most noticable in the
  19. header comment of the MVGEN routine which has undergone quite a few minor
  20. alterations during its lifetime.
  21.  
  22.     These sources are written for assembly using sass and might
  23. suffer compatibility problems when used with other assemblers.
  24.  
  25.     Any non-profit, non-commercial usage of these routines is permitted
  26. so long as they remain intact.  Any other usage requires my permission.
  27.  
  28.  
  29.                             Pauli
  30. seeya
  31.  
  32. Paul Dale               | Internet/CSnet:            grue@batserver.cs.uq.oz.au
  33. Dept of Computer Science| Bitnet:       grue%batserver.cs.uq.oz.au@uunet.uu.net
  34. Uni of Qld              | JANET:           grue%batserver.cs.uq.oz.au@uk.ac.ukc
  35. Australia, 4072         | EAN:                          grue@batserver.cs.uq.oz
  36.                         | UUCP:           uunet!munnari!batserver.cs.uq.oz!grue
  37. f4e6g4Qh4++             | JUNET:                     grue@batserver.cs.uq.oz.au
  38.  
  39.  
  40.  
  41. MVGEN:
  42. ;
  43. ; Routine written by Paul Dale November 1990
  44. ;   Copyright 1990 all rights reserved.
  45. ;
  46. ; Given a position in a reversi game (in SOS) produce a new
  47. ;   position (on TOS) that corresponds to the old position
  48. ;   after anoth move has been made.  Thus succesive calls
  49. ;   to this routine (without altering the stack) will cause
  50. ;   all the moves from the position (in SOS) to be made.  When
  51. ;   all legal moves have been done, the machine returns a five
  52. ;   nibble zero value at the start of SOS.  This routine requires
  53. ;   two strings of 112 characters or long at the top of the stack
  54. ;   it does not make any checks it assumes that they are present.
  55. ;   It also modifies the strings in situ.  Both of these are
  56. ;   potentially major problems, they have both been left in
  57. ;   in order to increase the speed of the program.  If a deep
  58. ;   minimax search is being performed then the time to copy each
  59. ;   of the positions would probably be longer than the time
  60. ;   required for the routine to run to completion.  Also, it
  61. ;   would be possible to play some rather nasty tricks moving
  62. ;   the stack pointer up and down the stack and making calls
  63. ;   to the move generation routine.  This would provide a major
  64. ;   speed increase in a deep search.  Initially, the program is
  65. ;   only going to make a single ply search so these considerations
  66. ;   are quite unwarranted (in the future this statement will
  67. ;   hopefully be false, i.e. a proper search will have be written)
  68. ;   Since the computer will move first (with respect to this
  69. ;   routine), the code has been written so that it will run more
  70. ;   quickly when the computer has the move.  In a more general,
  71. ;   deeper search this may still provide an advantage (whenever
  72. ;   an odd number of ply are searched).  Reconstructing the move
  73. ;   in a human readable form might prove interesting...  Also,
  74. ;   the search routine is designed to run faster when we are at
  75. ;   a terminal node (if there is a choice).  This is beacuse most
  76. ;   of the nodes that are going to be searched are terminal.
  77. ; One possible way to improve this routine would be to unroll the
  78. ;   important loops.  The initial copy could be done inline and
  79. ;   the piece run-follower could also be inlined (since runs can
  80. ;   only be 6 long and still be flippable).  The actual move
  81. ;   search shouldn't be inlined since the move subtable might be
  82. ;   subject to re-ordering (and it is 60 long, unrolling the thing
  83. ;   60 times is really stupid, but faster ;-)  Another interesting
  84. ;   thing might be to inline the code fo do_move (eight times only)
  85. ;   this would save a level of the stack and might speed things.
  86. ;   Again, I feel that this is not going to be worthwhile.
  87. ;
  88. ;
  89. ; The format of the board description string is as follows:
  90. ;
  91. ; 5     nibbles indicating the position in the move subtable
  92. ;       we're up to as an offset from the start.
  93. ; 1     nibble indicating the colour to move
  94. ; 5     nibbles padding, currently unused. (move made??)
  95. ; 91    nibbles that contain the board itself (1 nibble per
  96. ;       square.  A 9 by 10 grid with an extra square:
  97. ;       ##########
  98. ;       #........       Here # is an illegal border square
  99. ;       #........       and . is a vacant square
  100. ;       #........       X and O are player pieces
  101. ;       #...OX...
  102. ;       #...XO...       Start from the bottom left corner and
  103. ;       #........       work left to right up the page.
  104. ;       #........
  105. ;       #........
  106. ;       #########
  107. ; 122   nibbles that contain the move subtable.  Each byte
  108. ;       holds the offset from the start of the structure to
  109. ;       the appropriate square to move to.  This whole thing
  110. ;       is null terminated and shrinks as the game proceeds.
  111. ;       Hopefully this will provide an initial gross ordering
  112. ;       of the moves for the search.
  113. ;
  114. ; The initial position is thus:
  115. ;   66000 1 00000   start 0x00  initial info
  116. ;   888888888       start 0x0b  board structure itself
  117. ;   800000000       start 0x14
  118. ;   800000000       start 0x1d
  119. ;   800000000       start 0x26
  120. ;   80001f000       start 0x2f
  121. ;   8000f1000       start 0x38
  122. ;   800000000       start 0x41
  123. ;   800000000       start 0x4a
  124. ;   800000000       start 0x53
  125. ;   8888888888      start 0x5c  end of board, start of move table
  126. ;   51 c1 45 b5     start 0x66  corners
  127. ;   71 a1 72 e2 24 94 65 95     edge 2 away from corner
  128. ;   81 91 03 73 93 04 75 85     middle edge
  129. ;   12 22 13 63 a3 f3 e4 f4     middle one row/col in
  130. ;   02 32 82 d2 34 84 d4 05     knight move from corner
  131. ;   61 b1 e1 52 b4 25 55 a5     one away from corner
  132. ;   92 c2 44 74                 corners of middle 16 squares
  133. ;   a2 b2 23 53 b3 e3 54 64     rest of edge of middle 16
  134. ;   f1 42 c4 15                 the X squares
  135. ;   00                          Terminator
  136. ;   
  137. ;
  138. ; Internally, the pieces are stored as the follow nibbles:
  139. ;   0   Empty square
  140. ;   1   Human piece
  141. ;   f   Computer piece  NB: negation of computer code gives human
  142. ;   8   Border square
  143. ;
  144. ; On entry, it is assumed that the usual RPL things apply
  145. ;   later, this routine will likely be integrated into the
  146. ;   move evalulator and this assumption would be false.
  147. ;
  148. ; This routine makes use of the following registers:
  149. ;   d0, d1, a, b, c, d
  150. ;   r2, r3
  151. ;   p is used but it will be zero on exit (no matter what)
  152. ;   Also, bit 2 of ST is set if the human is moving and
  153. ;   bit 1 of ST is set if a valid move has been found (on exit)
  154. ;   bit 11 of ST is used to indicate that this is the bottom ply
  155. ;   to be searched, this means that the move generator doesn't
  156. ;   have to perform all updates of the board string that it usually
  157. ;   does.  (Set means this is the terminal ply).
  158. ;
  159. ;   Three levels of stack are used:
  160. ;       one for the CPU hardware flags (register st) and
  161. ;       one for internal subroutine usage and
  162. ;       one by the RPL calling routine.
  163. ;   If this were to be linked into a larger machine code block,
  164. ;   it would be possible to save the stack level for st and the
  165. ;   one for the RPL caller if this code was inlined.
  166. ;
  167. ; The registers are used for:
  168. ;   r0 returns the result from these routines
  169. ;   r2 holds a pointer to the body of SOS (after header stuff)
  170. ;   r3 holds a pointer to the body of TOS
  171. ;   d is used to hold the offset into the move sub-table
  172. ;
  173. ; Define some useful rom routines.  The first set are for
  174. ;   the 48sx and the second for the 28s.
  175. ;
  176. ; These are indicated in the source file by comments:
  177. ; INLINE CONSTANT
  178. ;
  179. ;save_regs      =   0x0679b ; Save RPL registers HP48sx
  180. ;restore_regs   =   0x067d2 ; Restore them HP48sx
  181. ;push_si        =   0x06537 ; Push R0 as sint, restore regs HP48sx
  182. ;pop_si         =   0x06641 ; Pop sint into A HP48sx
  183. ;
  184. ;save_regs      =   0x05081 ; Save RPL registers HP28s
  185. ;restore_regs   =   0x050b8 ; Restore them HP28s
  186. ;push_si        =   0x0???? ; Push R0 as sint, restore regs HP28s
  187. ;pop_si         =   0x04f27 ; Pop sint into A HP28s
  188. ;
  189.  
  190. mvgen_entry:
  191. ; before we save any registers, we are going to fudge with d1 so
  192. ; it appears that we've poped our args from the stack!
  193.     add.a   10, d1          ; two args!
  194.     inc.a   d
  195.     inc.a   d               ; pop the args properly
  196.     call.a  0x0679b         ; save regs INLINE CONSTANT
  197.     sub.a   10, d1          ; put them back
  198.  
  199.     clr.a   c               ; going to clear all the flags...
  200.     swap.x  c, st           ; grab the system flags
  201.     push.a  c               ; and save 'em
  202. ;   clrb    11, st          ; not bottom ply of search
  203.     call.3  minimax         ; do the real work
  204.  
  205.     pop.a   c               ; grab the system flags and
  206.     move.x  c, st           ; restore 'em to their former glory
  207.  
  208.     call.a  0x06537         ; push R0 on stack and restore regs INLINE CONSTANT
  209.     move.a  @d0,a           ; \
  210.     add.a   5,d0            ;  > return to RPL
  211.     jump    @a              ; /
  212.  
  213.  
  214. ;-----------------------------------------------------------------
  215. ; SUBROUTINE do_move: try to make a move in a given direction
  216. ;-----------------------------------------------------------------
  217. ;
  218. ; This is the one that really does all the work.  It expects
  219. ;   a direction of travel in c.a and it will attempt to flip
  220. ;   oponent disks in that direction.  If it succeeds in making
  221. ;   a flip, it should set a flag to indicate this.  It is free
  222. ;   to destroy the c register in any way whatsoever.  The
  223. ;   starting position should be in register d0 on entry and
  224. ;   should remain unchanged throughout this operation.  Register
  225. ;   d should also remain unchanged.  There is a trade off between
  226. ;   speed and register usage in the subroutine.  I wanted it to
  227. ;   run as fast as possible, but I couldn't accept using all the
  228. ;   registers.  Particularly, I did not desire to use any more
  229. ;   of the temporary Rn registers or levels of the stack.  If
  230. ;   this is only called from RPL these restrictions are silly.
  231. ;   However, I was/am intending to integrate this routines into
  232. ;   another (larger) machine code program.
  233. ;
  234. do_move:
  235.     move.a  c, b            ; save the offset somewhere safe
  236.     swap.a  a, d0           ; grab the initial square
  237.     move.a  a, d0           ; and store it back for safety
  238.     add.a   b, a            ; adjust by the offset
  239.     move.a  a, d1           ; prepare to examine that square
  240.     move.1  @d1, c
  241.     brbc    2, st, computer_move
  242.  
  243. human_move:
  244.     inc.a   c               ; test for computer's piece
  245.     brz.p   c, cont_move    ; it is we may have a move...
  246.     ret                     ; nope, give up looking
  247.  
  248. computer_move:
  249.     dec.a   c               ; check for human piece
  250.     retnz.p c               ; nope, give up
  251.  
  252. ;
  253. ; The first square has been check as the ememy piece, from here
  254. ;   we run along in that direction until we locate a square of
  255. ;   a different kind to the one we just located.  If this
  256. ;   different square is one of our pieces, then the move is
  257. ;   a valid one and we can flip all the pieces in teh middle
  258. ;   as if the move had been made.
  259. ;
  260. cont_move:
  261.     add.a   b, a            ; look at next sqaure in direction
  262.     move.a  a, d1
  263.     move.1  @d1, c          ; get the square value
  264.     retz.p  c               ; empty square - no good
  265.     move.p1 8, a
  266.     reteq.p c, a            ; off board square - no good
  267.     swap.a  a, d1           ; restore the pointer for next iter
  268.     brbc    2, st, c_move
  269.  
  270. h_move:
  271.     inc.a   c               ; is it a computer piece?
  272.     brz.p   c, cont_move    ; yep, keep following run
  273.     dec.a   c               ; restore to initial value
  274.     jump.3  valid_move      ; no, it must be a human piece
  275.  
  276. c_move:
  277.     dec.a   c
  278.     brz.p   c, cont_move
  279.     inc.a   c
  280.  
  281. ;
  282. ; Getting here implies that the move is a plausable one, we
  283. ;   must run backwards along the run and flip them all.  A
  284. ;   flag is also set to indicate that the move is valid.
  285. ;
  286. valid_move:
  287.     setb    1, st           ; this move is valid, so set flag
  288.     neg.a   b               ; we're going the other way this time
  289.  
  290. valid_loop:
  291.     add.a   b, a            ; backtrack one square
  292.     move.a  a, d1
  293.     move.1  @d1, a
  294.     retz.p  a               ; back to the initial square??
  295.     move.1  c, @d1          ; my piece goes there
  296.     swap.a  a, d1
  297.     jump.3  valid_loop      ; keep going until the empty square
  298.  
  299.  
  300. ;-----------------------------------------------------------------
  301. ; PROCEDURE minimax: Perform a minimax search.
  302. ;-----------------------------------------------------------------
  303. ;
  304. ; Minimax performs an alpha beta search to a depth given by TOS.
  305. ;   On entry there should be n+1 distinct copies of the board
  306. ;   on the stack (where n=TOS).  The top-most one is the one that
  307. ;   contains the initial position, the rest may contain anything
  308. ;   at all.  This routine returns the best move at the first ply
  309. ;   in register r0 (?).
  310. ; At present the search depth is limited to one move only.  And we
  311. ;   only perform a max search!
  312. ; Register r0 contains the best top-level move, r1 contains the
  313. ;   current max value.
  314. ;
  315. minimax:                    ; first setup registers r2, r3
  316.     swap.a  c, d1           ; grab the TOS string and save it
  317.     move.a  c, d1
  318.     move.a  c, d0
  319.     add.a   5, d1           ; point at SOS
  320.  
  321.     move.a  @d0, a          ; de-ref it and keep raw ptr
  322.     move.a  a, d0
  323.     add.a   10, d0          ; skip the header
  324.     swap.a  c, d0
  325.     move.w  c, r3           ; save TOS pointer
  326.     
  327.     move.a  @d1, a
  328.     move.a  a, d1
  329.     add.a   10, d1
  330.     swap.a  c, d1
  331.     move.w  c, r2           ; and the SOS pointer
  332.  
  333. ;
  334. ; Here is the main body of the maximise search
  335. ;
  336.     call.3  mvgen_main      ; generate the next move
  337.  
  338. ;
  339. ; now r2, r3 point at SOS, TOS respectively, C holds the move
  340. ;   sub-table offset or zero if no move was possible.  d0 points
  341. ;   to the square moved to.
  342. ;
  343.     brz.a   c, must_pass    ; no moves available means we pass
  344.  
  345.     call.3  eval_pos        ; find a static value for the position
  346. best_move:
  347.     move.w  b, a            ; grab the evaluation
  348.     move.w  a, r1           ; current best move
  349.     swap.a  a, d0
  350.     move.w  a, r0           ; it is the best so far
  351.  
  352. next_mv:                    ; figure out the next move
  353.     call.3  mvgen_main      ; perform the next move
  354.     brz.a   c, mm_exit      ; finished the search
  355.     call.3  eval_pos        ; score the position
  356.     move.w  r1, a           ; get the current score
  357.     brge.w  a, b, next_mv   ; continue with the next move unless...
  358.     jump.3  best_move       ; update best move found so far
  359.  
  360. must_pass:                  ; the only available move is to pass
  361.     clr.a   c
  362.     move.w  c, r0           ; return the zero move
  363.     ret
  364.  
  365. ; We've got a 'best' move and must return the offset of the move
  366. mm_exit:
  367.     move.w  r0, a           ; get the 'best' move
  368.     move.w  r3, c           ; get the start posn
  369.     neg.a   c               ; The assembler's sub.a ops are broken!!
  370.     add.a   c, a            ; work out the offset
  371.     move.w  a, r0           ; return the offset
  372.     ret
  373.  
  374.  
  375. ;-----------------------------------------------------------------
  376. ; SUBROUTINE mvgen_main: try to find a move for the given player
  377. ;-----------------------------------------------------------------
  378. ;
  379. ; The subroutine does all the real work.  The stuff above is just
  380. ;   an interface to the RPL caller.  The final production version
  381. ;   will not include the above.  This routine will return a zero
  382. ;   value in the C register if no (more) moves we found.  Register
  383. ;   d0 will contain the exact memory location of the square that
  384. ;   was just played into if a move was made.  It will contain
  385. ;   garbage otherwise.
  386. ;
  387. mvgen_main:                 ; The main routine itself
  388.     move.w  r2, c
  389.     move.a  c, d0           ; point at TOS
  390.     move.w  r3, c
  391.     move.a  c, d1           ; point at SOS
  392.  
  393. ;
  394. ; Here d0, d1 point at the raw strings.  We must now copy
  395. ;   d0 into d1 and then make the move.  The first few nibbles
  396. ;   of the string are special.  We've extract the colour info
  397. ;   and change the colour for the copied string.  We also get
  398. ;   the move offset stuff.
  399. ;
  400.     move.w  @d0, c          ; copy the first 16 nibbles
  401.     move.a  c, d            ; save the move index for later
  402.     move.1  5, p            ; point at the colour nibble
  403.     move.p1 0xf, a          ; compare value
  404. ;   clrb    2, st           ; assume computer moving
  405.     breq.p  c, a, mvg_skp1  ; skip player move code
  406.     setb    2, st           ; player's move
  407.  
  408. mvg_skp1:                   ; we now know who is moving
  409.     move.1  6, p            ; assume we're doing a partial copy
  410.     brbs    11, st, mvg_sht ; the short copy (for terminal nodes)
  411. ;
  412. ; Here goes the long copy code, for non-terminal nodes of the
  413. ;   search tree.  We have to change the colour of the player to
  414. ;   move.  Increase the loop count so the move table gets copied
  415. ;   and reset the move table index.
  416. ;
  417.     move.1  5, p            ; point back at the colour nibble
  418.     neg.p   c               ; change colour of the player to move
  419.     move.1  0, p            ; restore p to its normal state
  420.     move.p5 0x00066, c      ; reset the move table index
  421.     move.1  13, p           ; loop counter for full copy
  422.  
  423. mvg_sht:
  424.     move.w  c, @d1          ; write the first word back
  425.  
  426. copy_loop:                  ; The strings are 14*16 nibbles
  427.     add.a   16, d0          ; long.  This copies SOS into TOS
  428.     add.a   16, d1          ; Could unroll the loop for
  429.     move.w  @d0, a          ; greater speed (which shouldn't
  430.     move.w  a, @d1          ; be necessary yet)
  431.     dec.1   p
  432.     brne.1  p, 0, copy_loop ; keep doing it...
  433.  
  434. ; P must now be zero again
  435.  
  436. ;
  437. ; Here, TOS is basicly the same as SOS.  The move colour is
  438. ;   slightly different, but that is correct.  Now, we have
  439. ;   to run through the move list (starting at the offset)
  440. ;   looking for the next move to make.  If the null terminator
  441. ;   is found then we've got to return without making a move.
  442. ;
  443. ; Register usage:
  444. ;   d0 points to the interior of the string
  445. ;   r3 is TOS (start of body of string)
  446. ;   r2 is SOS (start of body of string)
  447. ;   d is the move offset
  448. ;
  449. ; This is the place to come when the previous move has been
  450. ;   rejected as a dud.
  451. ;
  452. next_square:
  453.     move.w  r3, c           ; grab TOS
  454.     add.a   d, c            ; square in board store
  455.     inc.a   d
  456.     inc.a   d               ; increment offset pointer
  457.  
  458.     move.a  c, d0
  459.     clr.a   a               ; need full 20 bits for add later
  460.     move.b  @d0, a          ; get the new offset
  461.     brz.b   a, done_all_m   ; we've looked at all moves :-{
  462.  
  463.     move.w  r3, c           ; grab TOS again
  464.     add.a   c, a            ; calc position
  465.     move.a  a, d0
  466.     move.p  @d0, c          ; get the piece at that square
  467. ;
  468. ; If the square is occupied, don't consider it any further.
  469. ;   It might be possible to dynamically update the move list
  470. ;   while the program is playing.  This is likely to be a messy
  471. ;   job since the ordering is quite important.
  472. ;
  473.     brnz.p  c, next_square  ; check if square is empty...
  474.  
  475. ;
  476. ; Here is where we've got to do the difficult work.  We've got
  477. ;   to verify if the suggested move is in fact legal and if
  478. ;   it is, we've got to make the move (in TOS) and return
  479. ;   normally.
  480. ;
  481. ; The constants to add for each move direction are:
  482. ;     8  9  10
  483. ;    -1  .   1
  484. ;   -10 -9  -8
  485. ;
  486. ; Bit 1 of the hardware status register is used to keep track
  487. ;   of the validity of the move.  If it is set then the move
  488. ;   is good.  If it is clear, the move is bad.
  489. ;   Register d0 still points at the initial square considered
  490. ;   for the move (a is a duplicate of this info -- not used).
  491. ;
  492.     clrb    1, st           ; assume the move is a dud
  493.  
  494.     clr.a   c
  495.     dec.a   c               ; left = -1
  496.     call.3  do_move
  497.  
  498.     clr.a   c
  499.     move.p1 8, c            ; up left = 8
  500.     call.3  do_move
  501.  
  502.     clr.a   c
  503.     move.p1 9, c            ; up = 9
  504.     call.3  do_move
  505.  
  506.     clr.a   c
  507.     move.p1 10, c           ; up right = 10
  508.     call.3  do_move
  509.  
  510.     clr.a   c
  511.     inc.a   c               ; right = 1
  512.     call.3  do_move
  513.  
  514.     move.p5 0xffff8, c      ; down right = -8
  515.     call.3  do_move
  516.  
  517.     move.p5 0xffff7, c      ; down = -9
  518.     call.3  do_move
  519.  
  520.     move.p5 0xffff6, c      ; down left = -10
  521.     call.3  do_move
  522.  
  523. ;
  524. ; By now, we've checked all moves from the given square and know
  525. ;   if the move was good or not (by checking bit 1 of ST).
  526. ;   If the move is bad, we've got to backtrack and try the next
  527. ;   potential move.  If it was good we can return now.  It
  528. ;   would be more usual to expect a dud move, so that is
  529. ;   checked for first.
  530. ;
  531.     brbc    1, st, next_square
  532.     jump.3  good_move       ; clean up and return
  533.  
  534. ; When the null terminator for a string is encountered, we
  535. ;   set the move offset (in SOS) to zero and exit normally).
  536. ;
  537. done_all_m:
  538.     clr.a   c
  539.     jump.3  set_move_offset ; & leave gracefully
  540.  
  541. ;
  542. ; Come here if the move that was considered has turned out to
  543. ;   be a legal move.  All we've got to do is update the move
  544. ;   index in SOS and return from the current position.  We must
  545. ;   also put a piece at the necessary location in TOS (as if
  546. ;   we've made the given move).  d0 points at the square where
  547. ;   we just moved to.
  548. ;
  549. good_move:
  550.     move.p1 0xf, c          ; assume computer piece = -1
  551.     brbc    2, st, pce_good ; depending upon colour
  552.     move.p1 1, c            ; human piece = 1
  553.  
  554. pce_good:
  555.     move.1  c, @d0          ; put the piece onto the board
  556.     move.a  d, c            ; get offset into move table and...
  557.  
  558. set_move_offset:
  559.     move.w  r2, a           ; and pointer to SOS body
  560.     move.a  a, d1
  561.     move.a  c, @d1          ; write the value
  562.     ret                     ; go back to the caller of this
  563.                             ; wonderous function
  564.  
  565.  
  566. ;-----------------------------------------------------------------
  567. ; eval_pos: Evalulate a given position using a simple eval func
  568. ;-----------------------------------------------------------------
  569. ;
  570. ; Evalulate the current position using a static evaluation function
  571. ;   Currently, this function is a simple piece differential one.
  572. ;   i.e. Count +1 for a computer piece, -1 for a human one and
  573. ;   0 for an empty square (code==0) or a border one (code=8)
  574. ;
  575. ;   We return the score for the position in register B.w and we are
  576. ;   free to use any registers we want except d0 which should remain
  577. ;   unchanged after calling this routine.  The current routine uses:
  578. ;   A for getting each square from the board
  579. ;   B to hold the partial evaluation
  580. ;   C for constants for testing against
  581. ;   D for the loop counter
  582. ;   d1 as a pointer into the board table
  583. ; The routine also assumes that the pointer to the current
  584. ;   position is in register r3 (which it should be since the
  585. ;   move_gen routine leaves it there).  No usage is made of the
  586. ;   fact that r2 points to the previous position.
  587. ;   (These registers may be destroyed if desired).
  588. ;
  589. eval_pos:
  590.     move.w  r3, a           ; get pointer to TOS
  591.     move.a  a, d1           ; point at what we desire
  592.     add.a   11, d1          ; get to the board itself
  593.     add.a   10, d1          ; skip initial bit of board
  594.     clr.a   c
  595.     move.p2 72, c           ; set 64 in C.a
  596.     move.a  c, d            ; save it in d
  597.     move.p2 64, c
  598.     clr.w   b               ; zero the entire register
  599.     move.a  c, b            ; score from 0 to 128
  600.  
  601. eval_lp:
  602.     move.1  @d1, a
  603.     move.p1 0xf, c          ; computer piece
  604.     brne.p  c, a, eval_sk1
  605.  
  606.     inc.a   b
  607.  
  608. eval_lp_end:
  609.     add.a   1, d1
  610.     dec.a   d
  611.     brnz.a  d, eval_lp
  612.     ret
  613.  
  614. eval_sk1:
  615.     move.p1 1, c            ; human piece
  616.     brne.p  c, a, eval_lp_end
  617.     dec.a   b
  618.     jump.3  eval_lp_end
  619.  
  620.  
  621.  
  622. ========================================================================
  623.  
  624. PUTN:
  625. ;
  626. ; This routine will put either a 1 or a -1 into the string
  627. ;   (third on stack) at the position specified by SOS.  The
  628. ;   value to be put is in TOS.  The string is updated in situ
  629. ;   and the other args are popped from the stack before
  630. ;   returning.
  631. ;
  632. ; These are indicated in the source file by comments:
  633. ; INLINE CONSTANT
  634. ;
  635. ;save_regs      =   0x0679b ; Save RPL registers HP48sx
  636. ;restore_regs   =   0x067d2 ; Restore them HP48sx
  637. ;push_si        =   0x06537 ; Push R0 as sint, restore regs HP48sx
  638. ;pop_si         =   0x06641 ; Pop sint into A HP48sx
  639. ;real_1         =   0x2a2c9 ; Real 1 (start of HP48sx rom real table)
  640. ;
  641. ;save_regs      =   0x05081 ; Save RPL registers HP28s
  642. ;restore_regs   =   0x050b8 ; Restore them HP28s
  643. ;push_si        =   0x0???? ; Push R0 as sint, restore regs HP28s
  644. ;pop_si         =   0x04f27 ; Pop sint into A HP28s
  645. ;real_1         =   0x112c9 ; Real 1 (start of HP28s rom real table)
  646. ;
  647.     move.a  @d1, c          ; get TOS == value
  648.     move.w  c, r0           ; chace it for later on
  649.     add.a   5, d1           ; pop stack
  650.     inc.a   d               ; inc free memory
  651.     call.a  0x06641         ; pop si, get offset in A.a INLINE
  652.     swap.a  c, d1           ; save this register for later
  653.     push.a  c
  654.     move.a  c, d1           ; put it back also
  655.     move.a  @d1, c          ; address of string
  656.     add.a   a, c            ; adjust by the offset
  657.     move.a  c, d1           ; into an address register
  658.     add.a   10, d1          ; adjust for header
  659.     move.w  r0, a           ; get the value back again
  660.     move.p5 0x2a2c9, c      ; check if it is 1 INLINE
  661.     breq.a  c, a, gtone     ; yep, it was a real 1
  662.     move.p1 15, c           ; -1
  663.     jump.3  common
  664.  
  665. gtone:
  666.     move.p1 1, c            ; 1
  667.  
  668. common:
  669.     move.1  c, @d1          ; put the thing into the string
  670.     pop.a   c
  671.     move.a  c, d1           ; restore for RPL
  672.     move.a  @d0, a          ; \
  673.     add.a   5, d0           ;  > return to RPL
  674.     jump.a  @a              ; /
  675.  
  676.  
  677.  
  678. ========================================================================
  679.  
  680.  
  681. GETN:
  682.  
  683. ;
  684. ; This routine, get the nth nibble from SOS where n is the
  685. ;   short int in TOS.  The 10 nibbles of header are skipped.
  686. ;   The nibble is returned as a real 0, 1, -1, or 8 only.  The
  687. ;   string is left on the stack unchanged.  (this code will
  688. ;   prob also work for 0..9 correctly!).
  689. ;
  690. ; These are indicated in the source file by comments:
  691. ; INLINE CONSTANT
  692. ;
  693. ;save_regs      =   0x0679b ; Save RPL registers HP48sx
  694. ;restore_regs   =   0x067d2 ; Restore them HP48sx
  695. ;push_si        =   0x06537 ; Push R0 as sint, restore regs HP48sx
  696. ;pop_si         =   0x06641 ; Pop sint into A HP48sx
  697. ;real_0         =   0x2a2b4 ; Real 0 (start of HP48sx rom real table)
  698. ;
  699. ;save_regs      =   0x05081 ; Save RPL registers HP28s
  700. ;restore_regs   =   0x050b8 ; Restore them HP28s
  701. ;push_si        =   0x0???? ; Push R0 as sint, restore regs HP28s
  702. ;pop_si         =   0x04f27 ; Pop sint into A HP28s
  703. ;real_0         =   0x112b4 ; Real 0 (start of HP28s rom real table)
  704. ;
  705.     swap.a  c, d1           ; get TOS
  706.     push.a  c               ; save it
  707.     move.a  c, d1           ; and restore everything
  708.     call.a  0x06641         ; pop TOS into A.a INLINE
  709.     move.a  @d1, c          ; get the string address
  710.     add.a   a, c            ; adjust by the desired offset
  711.     move.a  c, d1           ; into a pointer register
  712.     add.a   10, d1          ; past the header
  713.     clr.a   a
  714.     move.1  @d1, a          ; get the nibble into A.a
  715.     move.p1 15, c           ; check if it is -1
  716.     breq.p  c, a, m1        ; it is...
  717.     move.a  a, c            ; save it for later
  718.     add.a   a, a            ; *2
  719.     add.a   a, a            ; *4
  720.     add.a   a, c            ; *5 in C
  721.     add.a   a, a            ; *8
  722.     add.a   a, a            ; *16
  723.     add.a   c, a            ; *21 in A (== sizeof(real))
  724.  
  725. common:
  726.     move.p5 0x2a2b4, c      ; INLINE start of rom real table
  727.     add.a   c, a            ; index correctly
  728.     pop.a   c               ; get the old d1
  729.     move.a  c, d1
  730.     move.a  a, @d1          ; push the real value
  731.     dec.a   d               ; less space remains
  732.  
  733.     move.a  @d0, a          ; \
  734.     add.a   5, d0           ;  > return to RPL
  735.     jump.a  @a              ; /
  736. m1: clr.a   a
  737.     move.p2 0xd2, a         ; offset for the -1 real in table
  738.     jump.3  common          ; fix things up
  739.  
  740.  
  741.  
  742. ========================================================================
  743.  
  744.  
  745. CHKMV:
  746.  
  747. ;
  748. ; This function takes a string representing the board position
  749. ;   in level 2 and a short integer in level 1 that is the player's
  750. ;   move.  It checks to see if the move suggested is legal and
  751. ;   returns a real zero on the stack if it is.  A real one is
  752. ;   returned if the move is a dud.
  753. ;
  754. ; Define some useful rom routines.  The first set are for
  755. ;   the 48sx and the second for the 28s.
  756. ;
  757. ; These are indicated in the source file by comments:
  758. ; INLINE CONSTANT
  759. ;
  760. ;save_regs      =   0x0679b ; Save RPL registers HP48sx
  761. ;restore_regs   =   0x067d2 ; Restore them HP48sx
  762. ;push_si        =   0x06537 ; Push R0 as sint, restore regs HP48sx
  763. ;pop_si         =   0x06641 ; Pop sint into A HP48sx
  764. ;real_0         =   0x2a2b4 ; Real zero in rom HP48sx
  765. ;real_1         =   0x2a2c9 ; Real one in rom HP48sx
  766. ;
  767. ;save_regs      =   0x05081 ; Save RPL registers HP28s
  768. ;restore_regs   =   0x050b8 ; Restore them HP28s
  769. ;push_si        =   0x0???? ; Push R0 as sint, restore regs HP28s
  770. ;pop_si         =   0x04f27 ; Pop sint into A HP28s
  771. ;real_0         =   0x112b4 ; Real zero in rom HP28s
  772. ;real_1         =   0x112c9 ; Real one in rom HP28s
  773. ;
  774. mvchk_entry:
  775.     call.a  0x06641         ; Pop short integer into register A INLINE
  776.     call.a  0x0679b         ; Save RPL registers INLINE
  777.  
  778.     clr.a   c               ; going to clear all the flags...
  779.     swap.x  c, st           ; grab the system flags
  780.     push.a  c               ; and save 'em
  781.  
  782.     move.a  @d1, c          ; get the board position
  783.     move.a  c, d1           ; find out the colour on move
  784. ;   add.a   10, d1          ; skip the header
  785. ;   add.a   5, d1           ; to the move colour nibble
  786.     add.a   15, d1          ; to the colour nibble
  787.  
  788.     add.a   a, c            ; adjust by the move position
  789.     move.a  c, d0
  790.     add.a   10, d0          ; adjust for the header
  791.  
  792.     move.1  @d1, c          ; get the colour nibble
  793.     inc.p   c
  794.     brz.p   c, got_mover    ; computer moving?
  795.     setb    2, st           ; set player moving flag
  796. got_mover:
  797.  
  798.     brnz.a  a, check_normal
  799.  
  800. ;
  801. ; The player wants to pass, so we've got to try to generate
  802. ;   every move and if any of them is legal, the player cannot
  803. ;   pass.  d0 contains a pointer to the start of the board.
  804. ;
  805. check_pass:
  806.     add.a   11, d0          ; at start of logical board
  807.     add.a   10, d0          ; to real start of board
  808.     clr.a   c
  809.     move.p2 72, c
  810.     move.a  c, d            ; cache the loop counter
  811.  
  812. ;
  813. ; Run thought the board check for legal moves
  814. ;
  815. cp_loop:
  816.     move.1  @d0, c          ; load the square
  817.     brnz.p  c, cp_next      ; next iteration for full squares
  818.     call.3  check_move      ; is the move good?
  819.     brbs    1, st, ret_bad  ; good move means no pass!
  820.  
  821. cp_next:
  822.     add.a   1, d0           ; to next square
  823.     dec.a   d               ; check for end of search
  824.     brnz.a  d, cp_loop      ; keep going for all moves
  825.     jump.3  ret_good        ; pass is alright
  826.  
  827. ;
  828. ; Check if the move pointed to by d0 is legal.
  829. ;
  830. check_normal:
  831.     move.1  @d0, c          ; check that the suggested square is
  832.     brnz.p  c, ret_bad      ; really empty!
  833.     call.3  check_move      ; check if the move is valid
  834.     brbc    1, st, ret_bad  ; nope, it is a dud
  835.                             ; good moves fall through
  836. ;
  837. ; Now see if we can possibly make a move!
  838. ;
  839.  
  840. ret_good:
  841.     move.p5 0x2a2c9, c      ; get real one address INLINE
  842.     jump.3  ret_common
  843.  
  844. ret_bad:
  845.     move.p5 0x2a2b4, c      ; get real zero INLINE
  846.  
  847. ret_common:
  848.     move.a  c, a            ; save the value
  849.     call.a  0x067d2         ; restore RPL regs INLINE
  850.     move.a  a, @d1          ; replace TOS with our result
  851.  
  852.     pop.a   c               ; grab the system flags and
  853.     move.x  c, st           ; restore 'em to their former glory
  854.  
  855.     move.a  @d0,a           ; \
  856.     add.a   5,d0            ;  > return to RPL
  857.     jump    @a              ; /
  858.  
  859. ;
  860. ; Given a square, check if ANY move is legal and if so, set
  861. ;   flag 1.  (see code from mvgen.s for comments).  Set bit 1
  862. ;   if the move is good
  863. ;
  864. check_move:
  865.     clr.a   c
  866.     dec.a   c               ; left = -1
  867.     call.3  do_move
  868.  
  869.     clr.a   c
  870.     move.p1 8, c            ; up left = 8
  871.     call.3  do_move
  872.  
  873.     clr.a   c
  874.     move.p1 9, c            ; up = 9
  875.     call.3  do_move
  876.  
  877.     clr.a   c
  878.     move.p1 10, c           ; up right = 10
  879.     call.3  do_move
  880.  
  881.     clr.a   c
  882.     inc.a   c               ; right = 1
  883.     call.3  do_move
  884.  
  885.     move.p5 0xffff8, c      ; down right = -8
  886.     call.3  do_move
  887.  
  888.     move.p5 0xffff7, c      ; down = -9
  889.     call.3  do_move
  890.  
  891.     move.p5 0xffff6, c      ; down left = -10
  892. ;   call.3  do_move
  893. ;   ret
  894. ;   jump.3  do_move         ; optimised a little
  895.  
  896. ;
  897. ; try to perform the move, see mvgen.s for more details...
  898. ;   removed the code to actually make the move.
  899. ;
  900. do_move:
  901.     move.a  c, b            ; save the offset somewhere safe
  902.     swap.a  a, d0           ; grab the initial square
  903.     move.a  a, d0           ; and store it back for safety
  904.     add.a   b, a            ; adjust by the offset
  905.     move.a  a, d1           ; prepare to examine that square
  906.     move.1  @d1, c
  907.     brbc    2, st, computer_move
  908.  
  909. human_move:
  910.     inc.a   c               ; test for computer's piece
  911.     brz.p   c, cont_move    ; it is we may have a move...
  912.     ret                     ; nope, give up looking
  913.  
  914. computer_move:
  915.     dec.a   c               ; check for human piece
  916.     retnz.p c               ; nope, give up
  917.  
  918. cont_move:
  919.     add.a   b, a            ; look at next sqaure in direction
  920.     move.a  a, d1
  921.     move.1  @d1, c          ; get the square value
  922.     retz.p  c               ; empty square - no good
  923.     move.p1 8, a
  924.     reteq.p c, a            ; off board square - no good
  925.     swap.a  a, d1           ; restore the pointer for next iter
  926.     brbc    2, st, c_move
  927.  
  928. h_move:
  929.     inc.a   c               ; is it a computer piece?
  930.     brz.p   c, cont_move    ; yep, keep following run
  931.     dec.a   c               ; restore to initial value
  932.     jump.3  valid_move      ; no, it must be a human piece
  933.  
  934. c_move:
  935.     dec.a   c
  936.     brz.p   c, cont_move
  937.     inc.a   c
  938.  
  939. valid_move:
  940.     setb    1, st           ; this move is valid, so set flag
  941.     ret                     ; don't bother making the move, just return
  942. --
  943.  
  944.  
  945.